package com.dk.hi.gui.view;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;

import com.dk.hi.core.BlockMap;
import com.dk.hi.core.Direction;
import com.dk.hi.core.InputDTO;
import com.dk.hi.core.Settings;
import com.dk.hi.core.Speed;
import com.dk.hi.core.actor.Manner;
import com.dk.hi.core.helper.MapHelper;
import com.dk.hi.gui.MapActor;
import com.dk.hi.gui.comp.GraphicComponent;

public class ExplorationView extends GraphicComponent {
	/**
	 * The map
	 */
	private BlockMap map;

	/**
	 * The player
	 */
	private MapActor player;

	/**
	 * The actors on the map
	 */
	private List<MapActor> actors;

	/**
	 * @throws SlickException
	 */
	public ExplorationView() throws SlickException {
		this.actors = new ArrayList<MapActor>();
		this.map = new BlockMap("data/desert.tmx");

		this.player = new MapActor("data/characters/Actor2.png", Direction.DOWN, Speed.NORMAL);
		this.player.setOrigin(Settings.WINDOW_WIDTH / 2, Settings.WINDOW_HEIGHT / 2);

		MapActor npc = new MapActor("data/characters/Actor2.png", Direction.RIGHT, Speed.NORMAL);
		npc.setOrigin(50, 50);
		npc.setManner(Manner.FRIENDLY);
		this.actors.add(npc);
	}

	@Override
	protected void renderComponent(Graphics g) throws SlickException {
		// First, render the map
		this.map.render(g);

		List<MapActor> topActors = new ArrayList<MapActor>();
		List<MapActor> bottomActors = new ArrayList<MapActor>();
		for (MapActor actor : this.actors) {
			if (actor.getY() < this.player.getY()) {
				topActors.add(actor);
			} else {
				bottomActors.add(actor);
			}
		}

		// Render actors behind the player
		for (MapActor actor : topActors) {
			actor.render(g);
		}

		// Render player
		this.player.render(g);

		// Render actors in front of the player
		for (MapActor actor : bottomActors) {
			actor.render(g);
		}

		if (Settings.DEBUG_ENTITIES) {
			renderDebugInfo(g);
		}

	}

	@Override
	protected void updateComponent(GameContainer container, StateBasedGame game, int delta) throws SlickException {
		InputDTO input = buildInputDTO(container);

		// Handle player movement
		movePlayer(input);

		// Update each NPC
		for (MapActor actor : this.actors) {
			boolean isHovered = false;
			if (MapHelper.RECON_CIRCLE.contains(actor.getX() + 16, actor.getY() + 16)) {
				Rectangle s = new Rectangle(actor.getX(), actor.getY(), 32, 32);
				if (s.contains(input.getMouseX(), input.getMouseY())) {
					isHovered = true;
				}
			}
			actor.setHovered(isHovered);
			actor.update(input, this.player);
		}
	}

	/**
	 * Moves actors ont the BlockMap when the player moves
	 * 
	 * @param dx
	 *            - delta x
	 * @param dy
	 *            - delta y
	 */
	private void moveActors(int dx, int dy) {
		for (MapActor actor : this.actors) {
			actor.moveOrigin(dx, dy);
		}
	}

	/**
	 * Handle player movement on the map
	 * 
	 * @param input
	 *            - the input DTO
	 */
	private void movePlayer(InputDTO input) {
		if (input.isDown() || input.isUp() || input.isLeft() || input.isRight()) {
			// If the player pressed a move button

			// If an actor's menu is show, it must be destroyed :
			for (MapActor actor : this.actors) {
				actor.destroyMenu();
			}

			this.player.startMoving();
			int dx = 0;
			int dy = 0;
			int speed = 2;
			if (input.isLeft()) {
				this.player.setDirection(Direction.LEFT);
				dx = speed;
			} else if (input.isRight()) {
				this.player.setDirection(Direction.RIGHT);
				dx = -speed;
			}
			if (input.isUp()) {
				dy = speed;
				this.player.setDirection(Direction.UP);
			} else if (input.isDown()) {
				dy = -speed;
				this.player.setDirection(Direction.DOWN);
			}

			// We move the map according to the animation
			boolean cancelMove = false;
			this.map.moveOrigin(dx, dy);

			// Each actor must be moved to fit the new view
			moveActors(dx, dy);

			if (this.map.collide(this.player)) {
				// The player is in collision with the map.
				cancelMove = true;
			} else {
				for (MapActor actor : this.actors) {
					if (this.player.collide(actor)) {
						cancelMove = true;
						break;
					}
				}
			}

			if (cancelMove) {
				// There is a collision between the player and
				// something. We cancel the movement and stop
				// animation
				this.map.moveOrigin(-dx, -dy);
				moveActors(-dx, -dy);
				this.player.stopMoving();
			}
		} else {
			// Stop move animation
			this.player.stopMoving();
		}
	}

	private void renderDebugInfo(Graphics g) {
		g.draw(MapHelper.ACTION_CIRCLE);
		g.draw(MapHelper.RECON_CIRCLE);
	}
}
